﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using UpdaterForm.Annotations;
using ListView = System.Windows.Controls.ListView;
using UserControl = System.Windows.Controls.UserControl;

namespace UpdaterForm
{
    /// <summary>
    /// Interaction logic for AppControl.xaml
    /// </summary>
    public partial class AppControl : INotifyPropertyChanged
    {

        private ObservableCollection<UpdateInfo> _updates;
        private AppData _data;
        private BackgroundWorker _worker;
        private UpdateInfo _selectedUpdate;
     //   private String selectedVersion;

        public UpdateInfo SelectedUpdate
        {
            get { return _selectedUpdate; }
            set
            {
                _selectedUpdate = value;
                OnPropertyChanged("SelectedUpdate");
            }
        }

        public ObservableCollection<UpdateInfo> Updates
        {
            get
            {
                if (_updates == null)
                    _updates = new ObservableCollection<UpdateInfo>();

                return _updates;
            }
            set { _updates = value; }
        }

        public AppControl(AppData app)
        {
            InitializeComponent();
            _data = app;
            LoadUpdates();
            InitWorker();
        }

        private void InitWorker()
        {
            _worker = new BackgroundWorker();
            _worker.DoWork += Update;
            _worker.ProgressChanged += worker_ProgressChanged;
            _worker.RunWorkerCompleted += worker_RunWorkerCompleted;
            _worker.WorkerReportsProgress = true;
            _worker.WorkerSupportsCancellation = true;
        }


        private void InstallUpdate(UpdateInfo pUpdate)
        {
            var from = pUpdate.FullName;
            var to = _data.InstallationPath;



            this.Dispatcher.Invoke((Action)(() =>
            {
                PrbUpdateProcess.Maximum = pUpdate.Lenght;
                LogBox.Text = "";
            }));

            if (CopyDirectory(from, to,pUpdate.Log))
                SetCurrenVersion(pUpdate);

        }

        private void SetCurrenVersion(UpdateInfo pUpdate)
        {
            foreach (var update in Updates){
                if (update.Name.Equals(_data.CurrentVersion)){
                    update.IsInstalled = UpdateState.FullyInstalled;
                    break;
                }
            }
            _data.CurrentVersion =pUpdate.Name;
            pUpdate.IsInstalled = UpdateState.CurrentVesion;
            pUpdate.Log.Time = DateTime.Now;
            pUpdate.Log.State = UpdateState.FullyInstalled;
            _data.History.Logs.Add(pUpdate.Log);
        }

        private void LoadUpdates()
        {
            Updates.Clear();
        //    var updatesForldser = Path.Combine(Settings.Instance.RepositoryPath, _data.Name);
            var updatesForldser = Settings.Instance.RepositoryPath;

            foreach (var dir in DirectoryHelper.GetDirs(new DirectoryInfo(updatesForldser)))
            {
                string appUpdate = Path.Combine(dir.FullName, _data.AppName);
                if (!Directory.Exists(appUpdate))
                    continue;

               var updateDir = new DirectoryInfo(appUpdate);

                //TODO: Stanovitsa sliskom slozno nado perepabotat ierarhiju objectov
               var update = new UpdateInfo(updateDir){
                   IsInstalled = _data.History.GetStatus(updateDir.Parent.Name)
                };

               if (update.IsInstalled < UpdateState.FullyInstalled){
                    update.Log = _data.History.GetLog(updateDir.Parent.Name);
                }

               if (_data.CurrentVersion.Equals(update.Name)){
                    update.IsInstalled = UpdateState.CurrentVesion;
                }
               //TODO: Stanovitsa sliskom slozno nado perepabotat ierarhiju objectov - END
                Updates.Add(update);
            }
        }



        private void UpdateDirsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var di = ((ListView)sender).SelectedItem as UpdateInfo;

            if (di==null)
                return;

            SelectedUpdate = di;
            BtnInstall.IsEnabled = UpdateDirsList.SelectedItem != null;
        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            PrbUpdateProcess.Value = 0;
            PrbUpdateProcess.Maximum = 100;

            BtnInstall.IsEnabled = true;
            //TODO:save update log from listbox to logfile
        }

        void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.Dispatcher.Invoke((Action)(() =>
            {
                var action = sender as String;
                LogBox.AppendText(action + Environment.NewLine);
                if (PrbUpdateProcess.Value < PrbUpdateProcess.Maximum)
                    PrbUpdateProcess.Value++;
            }));
        }

        public bool CopyDirectory(string @from, string To, UpdateLog log)
        {
            var dir = new DirectoryInfo(from);
            if (!dir.Exists)
                return false;

            if (!Directory.Exists(To))
                Directory.CreateDirectory(To);

            foreach (var file in dir.GetFiles())
            {
                worker_ProgressChanged("Transfer: " + file.FullName, null);
                file.CopyTo(Path.Combine(To, file.Name), true);
                log.TransferedFiles.Add(file.FullName);
            }

            foreach (var subdir in dir.GetDirectories())
            {
                worker_ProgressChanged("Copy directory: " + subdir.FullName, null);
                CopyDirectory(subdir.FullName, Path.Combine(To, subdir.Name),log);
            }


            return true;
        }

        private void LogScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            LogScrollViewer.ScrollToEnd();
        }

        private void BtnRefresh_Click(object sender, RoutedEventArgs e)
        {
            LoadUpdates();
        }

        private void Update(object sender, DoWorkEventArgs e)
        {
            if (!SelectedHigherVersion())
                return;

            var updatesToInstall = new List<UpdateInfo>();
            foreach (var update in Updates)
            {
                if (update.IsInstalled > UpdateState.CurrentVesion)
                    updatesToInstall.Add(update);

                if (update.Equals(SelectedUpdate))
                    break;
            }

            if (updatesToInstall.Count > 0)
                foreach (var update in updatesToInstall)
                {
                    try
                    {
                        InstallUpdate(update);
                    }
                    catch (IOException ex)
                    {
                        SetAsFailed(update,ex.Message);
                        break;
                    }
                }
                    

        }

        private void SetAsFailed(UpdateInfo update, string message)
        {
            update.IsInstalled = UpdateState.InstallFailed;
            _data.History.Logs.Add(new UpdateLog { Name = update.Name, Time = DateTime.Now,State = UpdateState.InstallFailed,FailReason = message});
        }

        private bool SelectedHigherVersion()
        {
            try{
                var xDate = DateTime.ParseExact(SelectedUpdate.Name, Settings.Instance.DateFormat, null);
                DateTime yDate;
                try{
                    yDate = DateTime.ParseExact(_data.CurrentVersion, Settings.Instance.DateFormat, null);
                }
                catch{
                    yDate = DateTime.MinValue;
                }

                return xDate > yDate;
            }
            catch (Exception)
            {
                return false;
            }
        }

        private void BtnInstall_Click(object sender, RoutedEventArgs e)
        {
            BtnInstall.IsEnabled = false;
            _worker.RunWorkerAsync();
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
